#include "app/app_device.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "thirdparty/log.h"
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include "app/app_pool.h"


/// @brief 一个任务要注册到queue中 上游线程池的线程要执行的任务  用来本设备 revc_buffer 里面的数据读走
/// @param agv 
void QueueRecvTake(void *agv)
{
    //存储读取的数据
    char buf[128] = {0};
    Device *device = agv;
    //把recv_buffer 中数据读出
    Buffer_Read(&device->revc_buffer,buf,3);
    Buffer_Read(&device->revc_buffer,buf + 3,buf[1] + buf[2]);
    //调用上游外部设备 比如路由器 留下的 回调函数 把数据传递给他
    device->vptr->recv_callback(device,buf,buf[1] + buf[2] + 3);
}

/// @brief 一个务要注册到queue中 下游线程池的线程要执行的任务  用来本设备 wirte_buffer 里面的数据读走
/// @param agv 
void QueueSendTake(void *agv)
{
    Device *device = agv;
     char buf[1024];
    //把send——buffer数据读出
    Buffer_Read(&device->write_buffer,buf,3);
    Buffer_Read(&device->write_buffer,buf + 3,buf[1] + buf[2]);
    //把数据通过兼容曾提供的回调函数 处理成 他们能接受的格式
    int len = 0;
    if(device->vptr->post_write)
    {
        len =  device->vptr->post_write(device,buf,buf[1] + buf[2] +3);
    }
    log_trace("write_buf = %s",buf);
    //把数据发给设备  假日 设备是串口的话  设备就会自动到下游设备 就向32 给串口写了数据 对名读他的 rx就行
    //判断兼荣曾是否处理
    if(len)
    {
        //处理了
        write(device->fd,buf,len);
        log_trace("write scuueed");
    }
}


/// @brief 用来读下游设备比如蓝牙。。。 的数据的子线程的函数体
/// @param agv 
/// @return 
void * ReadDownstrDeciceChindThread(void *agv)
{
    //存储用来接受recvbuffer中的数据
    char buf[1024] = {0};
    Device *device = agv; 
    //创建一个任务
    Task task = {
        .task_func = device->vptr->recv_task,
        .argv = device
    };
    while(1)
    {
        //从设备中读数据
        int len = read(device->fd,buf,1024);
       if(len <= 0)
       {
            //表示没有都读到数据
            continue;
       }
        //把数据通过兼容曾的给的回调函数处理成我们可以接受的数据格式
        if(device->vptr->post_read)
        {
            //表示外部提供了这个函数需要进行处理
            len =  device->vptr->post_read(device,buf,len);
        }
        //把数据写入接受缓冲区
        if(len)
        {
            Buffer_Write(&device->revc_buffer,buf,len);
        }
        //注册一个任务 让上游的线程池把数据从recv——buffer 读出  并且调用外部设备 比如路由器给的回调函数 把数据传输过去
        Thread_RegisterTask(&task);
    }
}



/// @brief 设备的初始化函数
/// @param device 设备
/// @param fliename 设备名
/// @return succeed 0  fail -1
int app_device_init(Device *device,char * fliename)
{
    //判断设备是否以及初始化
    if(device->fd)
    {
        //说明设备以及被本函数初始化过了
        log_warn("app_device_init fail");
        return -1;
    }
    //给fliename 分配内存
    device->filename = malloc(strlen(fliename) + 1);
    //判断是否分配成功
    if(device->filename == NULL)
    {
        log_warn("app_device_init filename malloc men fial");
        //分配失败
        return -1;
    }

    //给device->vptr 分配内存 他就算一个指针 没有分内存厘米的需方法没有办法存储
    device->vptr = malloc(sizeof(struct vTable));
    //判断是否分配成功
    if(device->vptr == NULL)
    {
        //分配失败
        log_warn("app_device_init vptr malloc men fial");
        free(device->filename);
        return -1;
    }

    //创建一个设备 O_NOCTTY因为在linux里面串口可以是一个中段  当用程序打开一个终端默认该终端会是这个程序的控制终端
    //O_NOCTTY 这个可以避免这在情况
    device->fd = open(fliename,O_RDWR|O_NOCTTY);
    if(device->fd < 0)
    {
        log_warn("app_device_init open fliename fial");
        free(device->filename);
        free(device->vptr);
        return -1;
    }
    //给设备的读写缓存 开辟空间
    int result_revc =  Buffer_Init(&device->revc_buffer,16 * 1024);
    if(result_revc < 0)
    {
        log_warn("app_device_init revc_buffer mallco mem fial");
        free(device->filename);
        free(device->vptr);
        return -1;
    }

     int result_write =  Buffer_Init(&device->write_buffer,16 * 1024);

     if(result_write < 0)
    {
        log_warn("app_device_init write_buffer mallco mem fial");
        free(device->filename);
        free(device->vptr);
        Buffer_Free(&device->revc_buffer);
        return -1;
    }
    //给设备虚表  给上具体的函数指针  以及给一些设备属性给初始值
    //这个设备下是什么设备
    device->connect_type = CONNECT_NONE;  //下游连接那种类型的通讯设备
    device->is_running = 0;   //设备是否运行标识符
    strcpy(device->filename,fliename);  //设备名
    device->vptr->post_read = NULL;     //下游兼通成 需要体构函数 把数据变成我们需要
    device->vptr->post_write = NULL;    //下游兼通曾 需要提供函数 把数据变成他们能接受的
    //用来读下游设备比如蓝牙。。。 的数据的子线程的函数体
    device->vptr->background_func =  ReadDownstrDeciceChindThread;
    //一个任务要注册到queue中 上游线程池的线程要执行的任务  用来本设备 revc_buffer 里面的数据读走
    device->vptr->recv_task = QueueRecvTake;
    //一个任务要注册到queue中 下游线程池的线程要执行的任务  用来本设备 wirte_buffer 里面的数据读走
    device->vptr->send_task = QueueSendTake;
    //当接受到recv_buffer接受到数据 并且由上游线程池取出后 就执行这个函数  
    //是上游用来读这个设备的一个回调函数  这个函数有上游的路由器提供
    device->vptr->recv_callback = NULL;
    log_trace("Device init succeed");
    return 0;
}

/// @brief 启动设备
/// @param device 设备
/// @return succeed 0  fail -1
int app_device_start(Device *device)
{
    //判断设备是否以及启动
    if(device->is_running)
    {
        log_warn("app_device_start device double start");
        //设备以及被启动
        return -1;
    }
    //创建至子线程 用来读下游设备比如蓝牙。。。 的数据
    if(pthread_create(&device->pthread,NULL,device->vptr->background_func,device) < 0)
    {
        log_warn("app_device_start pthread create fail");
        //创建失败
        return -1;
    }
    //把设备启动标识符 变1
    device->is_running = 1;
    log_trace("device start succeed");
    return 0;
}


/**
 * @brief 特在设备的函数
 * @param
 * @param
 */
int app_device_stop(Device *device)
{
    //判断设备以及被启动
    if(device->is_running == 0)
    {
        log_warn("app_device_stop device not start");
        //为启动
        return -1;
    }
    //停止子线程  回收子线成
    if(pthread_cancel(device->pthread) > 0)
    {
        log_warn("app_device_stop pthread cancel fail");
        //停止失败
        return -1;
    }
    //回收子线程
    if(pthread_join(device->pthread,NULL) > 0)
    {
        log_warn("app_device_stop pthread join fail");
        //回收失败
        return -1;
    }
    //标志位  变0
    device->is_running = 0;
    log_trace("device stop succeed");
    return 0;
}


/// @brief 注册一个用来把自己这个串口设备的数据读走的函数 由上游设备提供 
/// @param device 
/// @param callback 
/// 当接受到recv_buffer接受到数据 并且由上游线程池取出后 就执行这个函数  
/// 是上游用来读这个设备的一个回调函数  这个函数有上游的路由器提供
/// @return 
void app_device_registerReceiveCallback(Device *device,int (*callback)(Device*,void*,int))
{
    device->vptr->recv_callback = callback;
}


/// @brief 用来把接收到的数据写入串口设备 并且注册一个任务 让线程从来区数据
/// @param device 
/// @param ptr 要写入的数据
/// @param len 
/// @return 
int app_device_write(Device *device, void *ptr, int len)
{
    char * aa = ptr;
    //把数据写入send——buffer  
    if(Buffer_Write(&device->write_buffer,ptr,len) < 0)
    {
        log_warn("app_device_write date write buffer fail");
        //写入失败
        return -1;
    }
    //创建一个任务
    Task task = {
        .task_func = device->vptr->send_task,
        .argv = device
    };
    //注册任务
    if(Thread_RegisterTask(&task) < 0)
    {
         log_warn("app_device_write Reaister task fail");
        //注册失败
        return -1;
    }
    log_trace("device send date succeed");
    return 0;
}



/// @brief 删除一个设备
/// @param device 
void app_device_close(Device *device)
{
    //释放内存
    close(device->fd);
    free(device->filename);
    Buffer_Free(&device->revc_buffer);
    Buffer_Free(&device->write_buffer);
    device->filename = NULL;
    device->is_running = -1;
}